



#pragma once

#include <cstring>
#include <iostream>
#include <string>
#include <functional>
#include <poll.h>
#include "sock.hpp"

namespace poll_ns{
    static const int defaultport = 8081;
    static const int num = 2048;
    static const int defaultfd = -1;

    using func_t = std::function<std::string(const std::string&)>;

    class PollServer{
    public:
        PollServer(func_t f, int port = defaultport) 
            : _func(f), _port(port), _listensock(-1), _rfds(nullptr)
        {}

        void initServer(){
            _listensock = Sock::Socket();
            Sock::Bind(_listensock, _port);
            Sock::Listen(_listensock);

            _rfds = new struct pollfd[num];
            for(int i = 0; i < num; i++) ResetItem(i);
            _rfds[0].fd = _listensock;
            _rfds[0].events = POLLIN;
        }

        void Print(){
            std::cout << "fd list: " << std::endl;
            for(int i = 0; i < num; i++) 
                if(_rfds[i].fd != defaultfd)
                    std::cout << _rfds[i].fd << ' ';
            std::cout << std::endl;
        }

        void ResetItem(int pos){
            _rfds[pos].fd = defaultfd;
            _rfds[pos].events = 0;
            _rfds[pos].revents = 0;
        }

        void Accepter(int listensock){
            logMessage(DEBUG, "in Accept");

            // 走到这里的时候，下面的acccept是不会阻塞的
            // select 告诉我 _listensock 读事件就绪了
            std::string clientip;
            uint16_t clientport = 0;
            // 新的 sock 拿上来后，队列里面就没有了 ---> 新连接从 telnet 断开后就不会一直打印 have event ready! 了，因为新 sock 被拿走了
            int sock = Sock::Accept(listensock, &clientip, &clientport);
            if(sock < 0) return ;
            logMessage(NORMAL, "accept success [%s : %d]", clientip.c_str(), clientport);

            // sock 我们能直接 recv / read 吗？ 不能，整个代码，只有select有资格检测事件是否就绪
            // 将新的sock托管给select !
            // 将新 sock 托管给 select 的本质，其实就是将 sock，添加到 fdarray 数组中即可(HandlerEvent 函数调用完成之后，start函数里面的再一次循环就会把新来的 sock, FD_SET(fdarray[i], &rfds) 给设置进去)
            // 遍历 fdarray 数组看最后一个可以填入 sock 的位置在哪里（用 int i = 0 和 for 循环来探测, 注意可能数组满了）
            int i = 0;
            for(i = 0; i < num; i++){
                if(_rfds[i].fd != defaultfd) continue;
                else break;
            }
            if(i == num){
                logMessage(WARNING, "server is full, please wait");
                close(sock);
            }
            else{
                _rfds[i].fd = sock;
                _rfds[i].events = POLLIN;
                _rfds[i].revents = 0;
            }

            Print();

            logMessage(DEBUG, "out Accept");
        }

        void Recver(int pos){
            logMessage(DEBUG, "in Recver");

            // 1. 读取 request
            // 这里的读取方式是有问题的(什么能不能读取到一个完整的报文之类的问题)
            char readbuffer[1024];
            ssize_t s = recv(_rfds[pos].fd, readbuffer, sizeof(readbuffer) - 1, 0);
            if(s > 0){
                readbuffer[s] = 0;
                logMessage(NORMAL, "clent# %s", readbuffer);
            }else if(s == 0){
                close(_rfds[pos].fd);
                ResetItem(pos);
                logMessage(ERROR, "client quit: %s", strerror(errno));
                return;
            }else{
                close(_rfds[pos].fd);
                ResetItem(pos);
                logMessage(ERROR, "client quit: %s", strerror(errno));
                return;
            }

            // 2. 处理 request
            std::string response = _func(readbuffer);

            // 3. 返回 response
            // write bug
            write(_rfds[pos].fd, response.c_str(), response.size());

            logMessage(DEBUG, "out Recver");
        }

        void HandlerEvent(){
            // 判断是什么事件就绪了：是监听事件_listensock, 还是除了_listensock之外的IO事件就绪了
            for(int i = 0; i < num; i++){
                if(_rfds[i].fd == defaultfd) continue;
                if(!(_rfds[i].events & POLLIN)) continue;

                if(_rfds[i].fd == _listensock && (_rfds[i].revents & POLLIN)){
                    // 上面的 if 判断语句里面的 i 可不能写成 0 的哦，因为 _listensock 在 fdarrray 数组里面的位置发生了变化的哦
                    Accepter(_rfds[i].fd);    
                }else if(_rfds[i].revents & POLLIN){
                    Recver(i);
                }else{

                }
            }
        }

        void start(){
            // >0
            // =0
            // <0 阻塞
            int timeout = -1;          
            for(;;){
                int n = poll(_rfds, num, timeout);
                switch(n)
                {
                case 0:
                    logMessage(NORMAL, "timeout...");
                    break;
                case -1:
                    logMessage(WARNING, "select error, code: %d, err: string: %s", errno, strerror(errno));
                    break;
                default:
                    logMessage(NORMAL, "have event ready!");
                    sleep(1); // 模拟事件处理
                    HandlerEvent(); 
                    break;
                }

                // 阻塞式 IO
                // std::string clientip;
                // uint16_t clientport = 0;
                // int sock = Sock::Accept(_listensock, &clientip, &clientport);
                // if(sock < 0) continue;
                // 开始进行服务器的处理逻辑
            }
        }

        ~PollServer(){
            if(_listensock < 0) close(_listensock);
            if(_rfds) delete[] _rfds;
        }
    private:
        int _port;
        int _listensock;
        struct pollfd* _rfds;
        func_t _func;
    };
}



